Skip to main content

Cloudflare Workers Setup

Cloudflare Workers is the recommended platform for EdgeMaster. This guide covers everything you need to deploy and manage EdgeMaster applications on Cloudflare's global edge network.


Why Cloudflare Workers?​

EdgeMaster is specifically optimized for Cloudflare Workers, offering:

  • ⚡ Zero Cold Starts - Instant response times globally
  • 🌍 Global Edge Network - 300+ data centers worldwide
  • 💰 Generous Free Tier - 100,000 requests/day free
  • 🔧 Built-in Services - KV, D1, R2, Durable Objects, AI
  • 📦 Zero Dependencies - ~14KB bundle size
  • 🚀 Instant Deployment - Deploy in seconds
  • 💵 Cost Effective - $5/month for 10M requests

Prerequisites​

Before starting, ensure you have:

  • Node.js 16.0.0 or higher
  • npm 7+ or yarn/pnpm
  • Cloudflare Account (Sign up free)
  • Wrangler CLI (we'll install this below)

Installation​

Step 1: Install Wrangler CLI​

Wrangler is Cloudflare's CLI tool for managing Workers:

npm install -g wrangler

Or use locally in your project:

npm install -D wrangler

Step 2: Login to Cloudflare​

Authenticate with your Cloudflare account:

npx wrangler login

This will open a browser window to authorize Wrangler.

Step 3: Create a New Project​

Initialize a new EdgeMaster project:

# Create project directory
mkdir my-edge-app
cd my-edge-app

# Initialize package.json
npm init -y

# Install EdgeMaster
npm install edge-master

# Install development dependencies
npm install -D wrangler @cloudflare/workers-types typescript

Configuration​

Create wrangler.toml​

Create a wrangler.toml file in your project root:

name = "my-edge-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"

# Optional: Enable observability
[observability]
enabled = true

# Optional: Configure build
# [build]
# command = "npm run build"

# Optional: Set account details
# account_id = "your-account-id"
# workers_dev = true

Configuration Options​

OptionDescriptionExample
nameWorker name (unique in your account)"my-edge-app"
mainEntry point file"src/index.ts"
compatibility_dateWorkers runtime version"2024-01-01"
account_idYour Cloudflare account ID"abc123..."
workers_devDeploy to workers.dev subdomaintrue

Create tsconfig.json​

Configure TypeScript for Cloudflare Workers:

{
"compilerOptions": {
"target": "ES2021",
"module": "ES2022",
"lib": ["ES2021"],
"types": ["@cloudflare/workers-types"],
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

Update package.json​

Add helpful scripts:

{
"name": "my-edge-app",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"deploy:production": "wrangler deploy --env production",
"tail": "wrangler tail",
"build": "tsc"
},
"dependencies": {
"edge-master": "^0.10.0"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20230419.0",
"typescript": "^5.0.4",
"wrangler": "^3.0.0"
}
}

Create Your Worker​

Basic Worker (src/index.ts)​

import { EdgeController, RouteHandler, Task, json } from 'edge-master';

const app = new EdgeController();

// Add routes
app.GET('/api/hello', new RouteHandler(new Task({
do: async () => json({
message: 'Hello from Cloudflare Workers!',
timestamp: new Date().toISOString()
})
})));

app.GET('/api/info', new RouteHandler(new Task({
do: async ({ req }) => {
return json({
url: req.url,
method: req.method,
headers: Object.fromEntries(req.headers)
});
}
})));

// Export fetch handler
export default {
fetch: (request: Request, env: any, ctx: ExecutionContext) => {
return app.handleRequest({
req: request,
env,
ctx
});
}
};

Access Cloudflare Environment​

export default {
fetch: (request: Request, env: any, ctx: ExecutionContext) => {
// env: Environment variables and bindings
// ctx: Execution context for waitUntil() and passThroughOnException()

return app.handleRequest({ req: request, env, ctx });
}
};

Local Development​

Run Development Server​

Start the local development server:

npm run dev

Or with Wrangler directly:

npx wrangler dev

Your worker will be available at:

  • Local: http://localhost:8787
  • Network: http://127.0.0.1:8787

Development Options​

# Run on custom port
npx wrangler dev --port 3000

# Enable live reload
npx wrangler dev --live-reload

# Use local bindings
npx wrangler dev --local

# Enable inspector
npx wrangler dev --inspect

Testing Locally​

Test your routes with curl:

# Test GET endpoint
curl http://localhost:8787/api/hello

# Test POST endpoint
curl -X POST http://localhost:8787/api/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'

Deployment​

Deploy to Production​

Deploy your worker to Cloudflare's global network:

npm run deploy

Or with Wrangler:

npx wrangler deploy

Your worker will be deployed to:

https://my-edge-app.<your-subdomain>.workers.dev

Deployment Output​

⛅️ wrangler 3.0.0
--------------------
Total Upload: 15.2 KB / gzip: 4.8 KB
Uploaded my-edge-app (1.2 sec)
Published my-edge-app (0.3 sec)
https://my-edge-app.your-subdomain.workers.dev
Current Deployment ID: abc-123-def

Production Deployment​

For production with custom domains:

npm run deploy:production

Environment Variables & Secrets​

Local Development Variables​

Create .dev.vars for local development:

# .dev.vars
API_KEY=dev-api-key-12345
JWT_SECRET=dev-jwt-secret
DATABASE_URL=https://dev-db.example.com
STRIPE_KEY=sk_test_123
caution

Never commit .dev.vars to version control. Add it to .gitignore.

Production Secrets​

Set production secrets using Wrangler:

# Set individual secrets
npx wrangler secret put API_KEY
# You'll be prompted to enter the value

npx wrangler secret put JWT_SECRET
npx wrangler secret put DATABASE_URL

List Secrets​

npx wrangler secret list

Delete Secrets​

npx wrangler secret delete API_KEY

Access Environment Variables​

export default {
fetch: (request: Request, env: any) => {
// Access environment variables
const apiKey = env.API_KEY;
const jwtSecret = env.JWT_SECRET;

// Use in your application
return app.handleRequest({ req: request, env });
}
};

Usage in handlers:

app.POST('/api/auth', new RouteHandler(new Task({
do: async ({ env }) => {
const jwtSecret = env.JWT_SECRET;

// Use secret for JWT signing
const token = await signJWT(payload, jwtSecret);

return json({ token });
}
})));

Workers KV (Key-Value Storage)​

Setup KV Namespace​

Create a KV namespace:

# Create production namespace
npx wrangler kv:namespace create "MY_KV"

# Create preview namespace (for development)
npx wrangler kv:namespace create "MY_KV" --preview

Configure in wrangler.toml​

Add KV bindings to your wrangler.toml:

name = "my-edge-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"

# KV Namespace binding
[[kv_namespaces]]
binding = "MY_KV"
id = "your-kv-namespace-id"
preview_id = "your-preview-kv-namespace-id"

Use KV in Your Worker​

app.GET('/api/cache/:key', new RouteHandler(new Task({
do: async ({ req, env }) => {
const url = new URL(req.url);
const key = url.pathname.split('/').pop();

// Read from KV
const value = await env.MY_KV.get(key);

if (!value) {
return notFound(`Key ${key} not found`);
}

return json({ key, value });
}
})));

app.POST('/api/cache/:key', new RouteHandler(new Task({
do: async ({ req, env }) => {
const url = new URL(req.url);
const key = url.pathname.split('/').pop();
const body = await parseJSON(req);

// Write to KV with optional expiration (in seconds)
await env.MY_KV.put(key, JSON.stringify(body.value), {
expirationTtl: 3600 // 1 hour
});

return json({ success: true, key });
}
})));

KV Commands​

# List namespaces
npx wrangler kv:namespace list

# List keys in namespace
npx wrangler kv:key list --binding=MY_KV

# Get value
npx wrangler kv:key get "my-key" --binding=MY_KV

# Put value
npx wrangler kv:key put "my-key" "my-value" --binding=MY_KV

# Delete key
npx wrangler kv:key delete "my-key" --binding=MY_KV

D1 Database (SQL)​

Create D1 Database​

# Create database
npx wrangler d1 create my-database

Configure in wrangler.toml​

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "your-database-id"

Use D1 in Your Worker​

app.GET('/api/users', new RouteHandler(new Task({
do: async ({ env }) => {
const { results } = await env.DB.prepare(
'SELECT * FROM users LIMIT 10'
).all();

return json({ users: results });
}
})));

app.POST('/api/users', new RouteHandler(new Task({
do: async ({ req, env }) => {
const { name, email } = await parseJSON(req);

const { success } = await env.DB.prepare(
'INSERT INTO users (name, email) VALUES (?, ?)'
).bind(name, email).run();

return json({ success }, { status: 201 });
}
})));

D1 Commands​

# Execute SQL
npx wrangler d1 execute my-database --command="CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"

# Execute SQL file
npx wrangler d1 execute my-database --file=schema.sql

# Query database
npx wrangler d1 execute my-database --command="SELECT * FROM users"

R2 Object Storage​

Create R2 Bucket​

npx wrangler r2 bucket create my-bucket

Configure in wrangler.toml​

[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"

Use R2 in Your Worker​

app.POST('/api/upload', new RouteHandler(new Task({
do: async ({ req, env }) => {
const file = await req.blob();
const filename = `${Date.now()}-file.jpg`;

// Upload to R2
await env.MY_BUCKET.put(filename, file);

return json({
success: true,
filename,
url: `https://my-bucket.r2.dev/${filename}`
});
}
})));

app.GET('/api/files/:filename', new RouteHandler(new Task({
do: async ({ req, env }) => {
const url = new URL(req.url);
const filename = url.pathname.split('/').pop();

// Get from R2
const object = await env.MY_BUCKET.get(filename);

if (!object) {
return notFound('File not found');
}

return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream'
}
});
}
})));

Custom Domains​

Add Custom Domain​

  1. Go to Cloudflare Dashboard
  2. Navigate to Workers & Pages
  3. Select your worker
  4. Click Triggers tab
  5. Click Add Custom Domain
  6. Enter your domain (e.g., api.example.com)

Configure Routes​

For zone-level routing, add routes in wrangler.toml:

routes = [
{ pattern = "api.example.com/*", zone_name = "example.com" },
{ pattern = "www.example.com/api/*", zone_name = "example.com" }
]

Monitoring & Logging​

View Logs (Tail)​

Stream live logs from your worker:

npm run tail

Or with Wrangler:

npx wrangler tail

Filter Logs​

# Filter by status
npx wrangler tail --status error

# Filter by HTTP method
npx wrangler tail --method POST

# Filter by sampling rate
npx wrangler tail --sampling-rate 0.5

Console Logging​

Add logging to your worker:

app.GET('/api/debug', new RouteHandler(new Task({
do: async ({ req }) => {
console.log('Request received:', req.url);
console.log('Method:', req.method);
console.log('Headers:', Object.fromEntries(req.headers));

return json({ debug: true });
}
})));

Best Practices​

1. Use TypeScript​

Always use TypeScript for type safety:

interface Env {
MY_KV: KVNamespace;
DB: D1Database;
API_KEY: string;
}

export default {
fetch: (request: Request, env: Env, ctx: ExecutionContext) => {
return app.handleRequest({ req: request, env, ctx });
}
};

2. Handle Errors Gracefully​

app.onError(async (error, { req, env }) => {
console.error('Error:', error);

// Log to external service
if (env.SENTRY_DSN) {
// await logToSentry(error);
}

return json({
error: 'Internal Server Error',
message: error.message
}, { status: 500 });
});

3. Use Cache API​

import { cacheInterceptor } from 'edge-master';

// Cache GET requests
app.addInterceptor(cacheInterceptor({
ttl: 3600, // 1 hour
shouldCache: (req) => req.method === 'GET'
}));

4. Set Resource Limits​

Monitor CPU time and memory usage to stay within limits:

  • CPU Time: 10ms (free) / 50ms (paid)
  • Memory: 128MB
  • Subrequest Limit: 50 (free) / 1000 (paid)

Troubleshooting​

Error: "Exceeded CPU time limit"​

Solution: Optimize async operations and reduce computation:

// ❌ Bad: Synchronous heavy computation
const result = heavyComputation();

// ✅ Good: Use async and optimize
const result = await optimizedAsyncComputation();

Error: "KV binding not found"​

Solution: Ensure KV namespace is configured in wrangler.toml and deployed:

npx wrangler deploy

Error: "Module not found: edge-master"​

Solution: Ensure EdgeMaster is installed:

npm install edge-master

Next Steps​

Now that your EdgeMaster app is running on Cloudflare Workers:

🚀 Scale Your Application​

🔒 Add Authentication​

💡 Explore Examples​


Additional Resources​


Happy deploying! 🚀